package com.xxxx.server.service.impl;

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xxxx.server.config.security.component.JwtTokenUtils;
import com.xxxx.server.mapper.AdminMapper;
import com.xxxx.server.mapper.AdminRoleMapper;
import com.xxxx.server.mapper.RoleMapper;
import com.xxxx.server.pojo.Admin;
import com.xxxx.server.pojo.AdminRole;
import com.xxxx.server.pojo.AdminVO;
import com.xxxx.server.pojo.RespBean;
import com.xxxx.server.pojo.RespPageBean;
import com.xxxx.server.pojo.Role;
import com.xxxx.server.service.IAdminService;
import com.xxxx.server.utils.AdminUtils;
import com.xxxx.server.utils.FileUtils;
import com.xxxx.server.utils.PoiXslUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author LHC
 * @since 2021-06-25
 */
@Service
public class AdminServiceImpl extends ServiceImpl<AdminMapper, Admin> implements IAdminService {

    @Autowired
    private AdminMapper adminMapper;//配置了扫描包，实际上已经注入了 但是还是爆红，不管

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private IAdminService adminService;

    //密码加密
    @Autowired
    private PasswordEncoder passwordEncoder;

    //密码加密
    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    @Autowired
    private JwtTokenUtils jwtTokenUtils;

    @Resource
    private FileUploadService fileUploadService;

    //将配置文件中存的值取过来
    @Value("${jwt.tokenHead}")
    private String tokenHead;

    @Autowired
    private RoleMapper roleMapper;

    @Autowired
    private AdminRoleMapper adminRoleMapper;

    /**
     * 登录之后返回token
     *
     * @param username
     * @param password
     * @param code
     * @param request
     * @return
     */
    @Override
    public RespBean login(String username, String password, String code, HttpServletRequest request) {
        //获取验证码
        String defaultKaptcha = (String) request.getSession().getAttribute("captcha");
        System.out.println("登录验证的验证码：" + code);
//        if (StringUtils.isEmpty(code) || !defaultKaptcha.equalsIgnoreCase(code)) {
//            return RespBean.error("验证码不正确，请重新输入！");
//        }

        System.out.println(username);
        //登录，admin 对象中没有权限字段
        Admin admin = adminService.getAdmin(username);
        if (admin == null || !password.equals(admin.getPassword())) {
            return RespBean.error("用户名或密码不正确，请重新输入！");
        }

//        if (admin == null || !bCryptPasswordEncoder.matches(password, admin.getPassword())) {
//            return RespBean.error("用户名或密码不正确，请重新输入！");
//        }
        System.out.println(admin);
        //判断账号是否被禁用
        if (!admin.getIntEnabled()) {
            return RespBean.error("账号被禁用,请联系管理员");
        }
        //这个获取的管理员用户有权限字段
        UserDetails userDetails = userDetailsService.loadUserByUsername(username);
        // 更新security登录用户对象
        // null 的位置本来该放密码，但是一般是不放密码的
        // userDetails.getAuthorities() 是权限列表
        System.out.println(userDetails.getAuthorities());
        UsernamePasswordAuthenticationToken authentication =
                new UsernamePasswordAuthenticationToken(userDetails, null,
                        userDetails.getAuthorities());
        // 放在security全局里面
        SecurityContextHolder.getContext().setAuthentication(authentication);

        // 到这里说明没问题 就让他拿到令牌
        String token = jwtTokenUtils.generateToken(userDetails);
        Map<String, String> tokenMap = new HashMap<>();
        tokenMap.put("token", token);
        tokenMap.put("tokenHead", tokenHead);//返回头部信息
        tokenMap.put("teacher", admin.getTeacher().toString());//角色，教师还是学生
        tokenMap.put("img", admin.getUserFace());//图片
        // 登录成功之后返回一个token给前端
        return RespBean.success("登录成功", tokenMap);//tokenMap中有 username，new Date（），tokenHead
    }

    /**
     * 根据用户名获取用户
     *
     * @param username
     * @return
     */
    @Override
    public Admin getAdminByUserName(String username) {
        //使用MybatisPlus的单个查询方法，selectOne 用.eq 比较方法在匹配查询条件

        Admin admin = adminMapper.selectOne(new QueryWrapper<Admin>().eq("username", username).eq("enabled", true));

        return admin;
    }

    /**
     * 根据用户id查询角色列表
     *
     * @param adminId
     * @return
     */
    @Override
    public List<Role> getRoles(Integer adminId) {

        return roleMapper.getRoles(adminId);
    }

    /**
     * 获取所有操作员
     *
     * @param keywords
     * @return
     */
    @Override
    public List<Admin> getAllAdmins(String keywords) {

        return adminMapper.getAllAdmins(AdminUtils.getCurrentAdmin().getId(), keywords);
    }

    /**
     * 更新操作员角色
     *
     * @param adminId
     * @param rids
     * @return
     */
    @Override
    @Transactional
    public RespBean updateAdminRole(Integer adminId, List<Integer> rids) {
        adminRoleMapper.delete(new QueryWrapper<AdminRole>().eq("adminId", adminId));

        Integer result = adminRoleMapper.addAdminRole(adminId, rids);
        if (result == rids.size())
            return RespBean.success("更新成功！");
        return RespBean.error("更新失败！");
    }

    /**
     * 个人中心 修改用户密码
     *
     * @param oldPass
     * @param pass
     * @param adminId
     * @return
     */
    @Override
    public RespBean updateAdminPassWord(String oldPass, String pass, Integer adminId,
            String phone, String userFace, String address) {
//        Admin admin = adminMapper.selectById(adminId);
//        // 密码是加密过的
//        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
//        System.out.println(admin.getPassword() + "===" + oldPass);
//        // 比较旧密码是否和未更新的数据库密码是否一样，不一样则不让修改
//        if (encoder.matches(oldPass, admin.getPassword())) {
//            // 设置新密码，加密
//            admin.setPassword(encoder.encode(pass));
//            int result = adminMapper.updateById(admin);
//            System.out.println(result);
//            if (result == 1) {
//                return RespBean.success("更新成功!");
//            }
//        }else {
//            return RespBean.error("旧密码不正确，！");
//        }
//        return RespBean.error("更新失败！");

        Admin admin = adminMapper.selectById(adminId);
        // 比较旧密码是否和未更新的数据库密码是否一样，不一样则不让修改
        if (oldPass.equals(admin.getPassword())) {
            // 设置新密码，加密
            admin.setPassword(pass);
            if (!StringUtils.isEmpty(phone)) {
                // 设置电话
                admin.setPhone(phone);
            }
            admin.setAddress(address);
            admin.setUserFace(userFace);
            int result = adminMapper.updateInfo(admin);
            if (result == 1) {
                return RespBean.success("更新成功!");
            }
        } else {
            return RespBean.error("旧密码不正确，修改失败！");
        }
        return RespBean.error("更新失败！");
    }

    /**
     * 更新用户头像
     *
     * @param url
     * @param id
     * @param authentication
     * @return
     */
    @Override
    public RespBean updateAdminUserFace(String url, Integer id, Authentication authentication) {
        Admin admin = adminMapper.selectById(id);
        admin.setUserFace(url);
        int result = adminMapper.updateById(admin);
        if (result == 1) {
            //更新当前用户的全局信息
            Admin principal = (Admin) authentication.getPrincipal();
            principal.setUserFace(url);
            SecurityContextHolder.getContext().setAuthentication(
                    new UsernamePasswordAuthenticationToken(admin, null, authentication.getAuthorities()));
            return RespBean.success("更新成功！", url);
        }
        return RespBean.error("更新失败！");
    }

    /**
     * 获取管理员
     *
     * @param username
     * @return
     */
    @Override
    public Admin getAdmin(String username) {
        Admin admin = adminMapper.getAdmin(username);
        return admin;
    }

    /**
     * 添加用户
     *
     * @param admin
     */
    public RespBean addOrUpdateAdmin(Admin admin) {
        if (admin.getId() < 1) {
            // 判断用户名是否重复
            Admin exist = adminMapper.getAdmin(admin.getUsername());
            if (Objects.nonNull(exist)) {
                // 报错
                return RespBean.error("该用户名已存在！");
            }
            String username = admin.getUsername();
            try {
                // 设置密码默认为用户名后6位
                admin.setPassword(username.substring(username.length() - 6));
            } catch (Exception e) {
                return RespBean.error("用户名不能少于6位字符!");
            }
            // 默认启用
            admin.setEnabled(true);
            // 添加
            adminMapper.addAdmin(admin);
            return RespBean.success("添加成功!");
        } else {
            AdminVO adminById = adminMapper.getAdminInfoById(admin.getId());
            // 判断用户有无修改账号
            if (!adminById.getUsername().equals(admin.getUsername())) {
                // 判断用户名是否重复
                Admin exist = adminMapper.getAdmin(admin.getUsername());
                if (Objects.nonNull(exist)) {
                    return RespBean.error("该用户名已存在！");
                }
            }
            adminMapper.updateAdminInfo(admin);
            return RespBean.success("修改成功！");
        }
    }

    /**
     * 用户详情
     *
     * @param id
     * @return
     */
    public AdminVO selectAdminById(Integer id) {

        return adminMapper.getAdminInfoById(id);
    }

    /**
     * 删除用户
     *
     * @param id
     * @return
     */
    public RespBean deleteAdmin(Integer id) {
        if (id == 1) {
            return RespBean.error("操作失败，系统管理员无法删除！");
        }
        if (adminMapper.deleteAdmin(id) > 0) {
            return RespBean.success("删除成功！");
        }
        return RespBean.error("删除失败！");
    }

    /**
     * 分页
     *
     * @param pageInt
     * @param pageSize
     */
    public RespPageBean getPage(Integer pageInt, Integer pageSize, String text) {
        if (!StringUtils.isEmpty(text)) {
            text = "%" + text + "%";
        }
        //开启分页
        Page<AdminVO> page = new Page<>(pageInt, pageSize);
        IPage<AdminVO> employeePage = adminMapper.getAdminPage(page, text);
        // 分页记录
        List<AdminVO> records = employeePage.getRecords();
        // employeePage.getTotal() 总记录数， employeePage.getRecords() 查询出来的集合(记录)
        RespPageBean respPageBean = new RespPageBean(employeePage.getTotal(), records);
        return respPageBean;
    }

    /**
     * 批量导入
     *
     * @param multipartFile
     * @return
     */
    public RespBean batchImport(MultipartFile multipartFile) throws IOException {
        if (multipartFile != null && !StringUtils.isEmpty(multipartFile.getOriginalFilename())) {
            // 检验文件格式
            boolean b = PoiXslUtils.checkIsExcel(multipartFile.getOriginalFilename());
            if (b) {
                RespBean.error("只能上传excel文件！");
            }
        }
        // 记录错误的数据
        List<String> errList = new ArrayList<>();
        // 生成文件到临时目录
        String filePath = FileUtils.uploadFileTemp(multipartFile);
        // 解析文件，第一个list是标题集合
        List<List<String>> rowList = PoiXslUtils.readExcel(filePath);
        // 模板标题
        List<String> list = new ArrayList<>();
        list.add("学生学号");
        list.add("姓名");
        // 文件是否为空
        if (CollectionUtils.isEmpty(rowList) || rowList.size() == list.size()) {
            return RespBean.error("模板文件内容不能为空！");
        }
        // 判断集合标题是否相同
        if (rowList.get(0).containsAll(list)) {
            // 从第二个数据行开始遍历(第一个集合是标题行)
            for (int i = 1; i < rowList.size(); i++) {
                // 获取列值
                List<String> valueList = rowList.get(i);
                // 操作对象
                Admin admin = new Admin();
                // 学号
                String number = valueList.get(0);
                // 姓名
                String name = valueList.get(1);
                // 判断用户名是否重复
                Admin exist = adminMapper.getAdmin(number);
                if (Objects.nonNull(exist)) {
                    // 报错
                    errList.add("学号为【" + number + "】的学生已经存在");
                    break;
                }
                // 判断学号长度
                if (number.length() <= 8) {
                    // 报错
                    errList.add("学号为【" + number + "】的数据错误，不能少于8位字符导入");
                    break;
                }
                // 设置账号
                admin.setUsername(number);
                // 设置密码，账号后6位
                admin.setPassword(number.substring(number.length() - 6));
                // 设置姓名
                admin.setName(name);
                // 默认启用
                admin.setEnabled(true);
                // 设置角色位学生
                admin.setTeacher(false);
                // 设置默认头像
                admin.setUserFace("https://z3.ax1x.com/2021/11/06/IKTMuV.jpg");
                // 添加
                adminMapper.addAdmin(admin);
            }
        } else {
            return RespBean.error("模板文件格式不正确，请重新下载模板文件在提交！");
        }
        // 添加错误记录
        for (String errInfo : errList) {
            adminMapper.addErrorRecord(errInfo, "user");
        }
        // 没有不删除
        if (!StringUtils.isEmpty(filePath)) {
            // 删除文件
            FileUtils.deleteFile(filePath);
        }
        return RespBean.success("添加成功，请查看导入记录有无错误数据！");
    }

    /**
     * 获取模板文件
     *
     * @return
     */
    public RespBean getTemplate(String path) {
        // 模板文件路径
        String filePath = FileUtils.getAbsPath() + "template.xlsx";
        // 创建文件夹

        path = "D:" + File.separator + "教学系统文件夹" + File.separator;
        File file = new File(path);
        if (!file.exists()) {//如果文件夹不存在
            file.mkdir();//创建文件夹
        }

        FileUtils.fileCopy(filePath, path + "用户导入模板-" + System.currentTimeMillis() + ".xlsx");
        return RespBean.success("文件已下载至磁盘--》" + path);
    }

    /**
     * 导出学生信息
     *
     * @param response
     */
    public void userList(HttpServletResponse response) {
        List<Admin> adminList = adminMapper.getUserList();
        // 导出员工信息基本设置
        // title：文件内容中的标题名，第一行 sheetName：文件中的表名 ExcelType:导出的表格文件名后缀， 1.HSSF 后缀为.xls，2.XSSF 为 .xlsx，
        // 2003版本的导出速度更快，并且用 2003 或者 2003 以上的office都能打开，2007版本的office只能向上兼容
        ExportParams exportParams = new ExportParams("学生信息", "学生信息", ExcelType.HSSF);
        // 查询到的 list 导出的表格数据，此时还没有输出文件
        Workbook sheets = ExcelExportUtil.exportExcel(exportParams, Admin.class, adminList);

        BufferedOutputStream outputStream = null;
        try {
            // 以流的形式输出,防止文件乱码
            response.setContentType("application/octet-stream");
            // 防止下载出来的文件名中文乱码
            // URLEncoder.encode("员工信息表.xls","UTF-8") ： 输出的文件名并且设置编码
            response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode("员工信息表.xls", "UTF-8"));
            // 拿到输出流
            outputStream = new BufferedOutputStream(response.getOutputStream());
            // 导出的表格数据，以流的形式输出，提供给浏览器下载
            sheets.write(outputStream);
            outputStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
